﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace Platform.Storage.Impl
{
	internal static class FieldNameExtensions
	{
		internal static string GetFieldName( this Expression node )
		{
			return node.GetFieldName( null );
		}

		internal static string GetFieldName( this Expression node, IEnumerable<ParameterExpression> parameters )
		{
			var resolver = new FieldNameVisitor();

			if ( parameters != null )
				resolver.Parameters.AddRange( parameters );

			return resolver.GetFieldName( node );
		}
	}

	
	/// <summary>
	/// Percorre a árvore de expressões e identifica qual campo 
	/// da entidade está sendo utilizado para fazer a pesquisa
	/// </summary>
	internal sealed class FieldNameVisitor : ExpressionVisitor
	{
		/// <summary>
		/// 
		/// </summary>
		public FieldNameVisitor()
		{
			this.Parameters = new List<ParameterExpression>();
		}

		/// <summary>
		/// 
		/// </summary>
		public List<ParameterExpression> Parameters
		{ get; private set; }

		public string FieldName
		{ get; private set; }

		protected override Expression VisitBinary( BinaryExpression node )
		{
			switch ( node.NodeType )
			{
				case ExpressionType.Equal:
				case ExpressionType.NotEqual:
				case ExpressionType.GreaterThan:
				case ExpressionType.GreaterThanOrEqual:
				case ExpressionType.LessThan:
				case ExpressionType.LessThanOrEqual:
					bool expandLeft = node.Left.Has<ParameterExpression>( p => this.Parameters.Contains( p ) );
					bool expandRight = node.Right.Has<ParameterExpression>( p => this.Parameters.Contains( p ) );

					if ( expandLeft )
						this.FieldName = node.Left.GetPropertyName();
					else if ( expandRight )
						this.FieldName = node.Right.GetPropertyName();

					return node;
				default:
					return base.VisitBinary( node );
			}
		}

		protected override Expression VisitUnary( UnaryExpression node )
		{
			switch ( node.NodeType )
			{
				case ExpressionType.Quote:
					return base.Visit( node.Operand );
				default:
					return base.VisitUnary( node );
			}
		}

		protected override Expression VisitLambda<T>( Expression<T> node )
		{
			if ( node.Parameters.Count > 0 )
				this.Parameters.AddRange( node.Parameters );

			return base.Visit( node.Body );
		}

		public string GetFieldName( Expression node )
		{
			this.FieldName = String.Empty;
			base.Visit( node );
			return this.FieldName;
		}
	}
}
